#include <windows.h>

#include <stdio.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <math.h>
#include "CGfxOpenGL.h"
#include "RoboArm.h"

// disable implicit float-double casting
#pragma warning(disable:4305)

// angle increment for cone slices
const int DAngle = 5;

RoboArm roboArm(0, 90, 5, 5);

extern int PosX;
extern int PosY;
extern int PosZ;
extern int LookX;
extern int LookY;
extern int LookZ;
extern int TransOffset;
extern int Roll;
extern int FOV; 
extern Vertex3F SphereCenter; 

// returns the xz length of the vertex for a cone slice
float XZLength(Vertex3F *v) {
	return (float)(sqrt(v->x * v->x + v->z * v->z));
}

float MIN(float a, float b) {
    if (a < b) return a;
    else return b;
}

int MIN(int a, int b) {
    if (a < b) return a;
    else return b;
}
int MAX(int a, int b) {
    if (a > b) return a;
    else return b;
}

float MAX(float a, float b) {
    if (a > b) return a;
    else return b;
}
    
float MIN(float a, float b, float c) {
    return MIN(MIN(a, b), c);
}

float MAX(float a, float b, float c) {
    return MAX(MAX(a, b), c);
}



CGfxOpenGL::CGfxOpenGL()
{
}

CGfxOpenGL::~CGfxOpenGL()
{
}

bool CGfxOpenGL::Init()
{   
    // clear to black background
    //glClearColor(1.0, 1.0, 1.0, 0.0);
    glClearColor(0.5, 0.5, 0.5, 0.0);

    // initialize a rotation angle
    m_angle = 0.0f;

    quadric = gluNewQuadric();


    return true;
}

bool CGfxOpenGL::Shutdown()
{
    if (quadric != NULL) {
        delete quadric;
        quadric = NULL;
    }
    return true;
}

void CGfxOpenGL::SetupProjection(int width, int height)
{
    if (height == 0)                    // don't want a divide by zero
    {
        height = 1;                 
    }

    glViewport(0, 0, width, height);        // reset the viewport to new dimensions
    glMatrixMode(GL_PROJECTION);            // set projection matrix current matrix
    glLoadIdentity();                       // reset projection matrix

    // calculate aspect ratio of window
    gluPerspective(45.0f,(GLfloat)width / (GLfloat)height, 1.0f, 1000.0f);

    glMatrixMode(GL_MODELVIEW);             // set modelview matrix
    glLoadIdentity();                       // reset modelview matrix

    m_windowWidth = width;
    m_windowHeight = height;
}

void CGfxOpenGL::Prepare(float dt)
{
    m_angle += dt;
}


void xZFloor(double y) {
	int wdQuads = 10;           // number of quad faces across (in x)
	int htQuads = 10;           // number fo quad faces into or out of the screen (in y)
	double dx = 10.0;           // horiz space between vertices
	double dz = 10.0;           // vert space between vertices
	double x, z;             // x, y, z, for each vertex
	int iz, ix;                 // integer counters in x and y directions
	int wdVertices, htVertices; // wd & ht of grid in vertices
	int quadX, quadZ;           // integer coutners for each quad in x and y directions
	int topLeft, topRight, bottomLeft, bottomRight; // 1-based index for each vertex of the quad
    Vertex3F* terrain;

    // calc number of vertices
	wdVertices = wdQuads + 1;
	htVertices = htQuads + 1;

    terrain = new Vertex3F[wdVertices * htVertices];

    // calc the x, y , z for each vertex
	for (iz=0; iz < htVertices; iz++) {
		for (ix=0; ix < wdVertices; ix++) {
			x = ix * dx;
			z = iz * -dz;

            terrain[iz * wdVertices + ix].x = x;
            terrain[iz * wdVertices + ix].y = y;
            terrain[iz * wdVertices + ix].z = z;
            terrain[iz * wdVertices + ix].r = 1.0;
            terrain[iz * wdVertices + ix].g = 1.0;
            terrain[iz * wdVertices + ix].b = 1.0;
		}
	}


    // calc the vertex indices for each triangle
    // loop through each quad in the grid
	for (quadZ=0; quadZ < htQuads; quadZ++) {
		for (quadX=0; quadX < wdQuads; quadX++) {
            // calc the indexes of the 4 corners of the quad
			topLeft = quadZ * wdVertices + quadX;
            topRight = topLeft + 1;
			bottomLeft = topLeft + wdVertices;
			bottomRight = bottomLeft + 1;

            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

            glBegin(GL_QUADS);
                glColor3f(terrain[bottomLeft].r, terrain[bottomLeft].g, terrain[bottomLeft].b);
                glVertex3f(terrain[bottomLeft].x, terrain[bottomLeft].y, terrain[bottomLeft].z);

                glColor3f(terrain[bottomRight].r, terrain[bottomRight].g, terrain[bottomRight].b);
                glVertex3f(terrain[bottomRight].x, terrain[bottomRight].y, terrain[bottomRight].z);

                glColor3f(terrain[topRight].r, terrain[topRight].g, terrain[topRight].b);
                glVertex3f(terrain[topRight].x, terrain[topRight].y, terrain[topRight].z);

                glColor3f(terrain[topLeft].r, terrain[topLeft].g, terrain[topLeft].b);
                glVertex3f(terrain[topLeft].x, terrain[topLeft].y, terrain[topLeft].z);

            glEnd();
		}
	}

    delete[] terrain;
}

Vertex3F CGfxOpenGL::ScreenToWorld(int mousex, int mousey) {
    // converts mouse coords x, y to world coords on the xy plane
    double modelview[16], projection[16];
    int viewport[4];
    float z;
    double wx, wy, wz;
    double scale;
    Vertex3F result;

    // clear the depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     

    // make sure we draw a solid poly so that the depth buffer is filled
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);


    // draw a plane at z=0 to fill depth buffer with correct values
    // for GL_DEPTH_COMPONENT below
    glBegin(GL_QUADS);
        glVertex3f(-500, -500, 0);
        glVertex3f( 500, -500, 0);
        glVertex3f( 500,  500, 0);
        glVertex3f(-500,  500, 0);
    glEnd();

    //get the projection matrix		
    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    //get the modelview matrix		
    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
    //get the viewport		
    glGetIntegerv( GL_VIEWPORT, viewport );

    //Read the window z co-ordinate 
    //(the z value on that point in unit cube)		
    glReadPixels( mousex, viewport[3]-mousey, 1, 1,
    GL_DEPTH_COMPONENT, GL_FLOAT, &z );

    //Unproject the window co-ordinates to 
    //find the world co-ordinates.
    gluUnProject( mousex, viewport[3]-mousey, z, modelview, projection, viewport, &wx, &wy, &wz );

    static char str[128];
    sprintf(str, "<%d %d %f>  ---> <%0.2f, %0.2f, %0.2f>\n", mousex, mousey, z, wx, wy, wz);
    //OutputDebugString(str);
    //SetWindowText(HDialog, str);

    result.x = wx;
    result.y = wy;
    result.z = 0;

    // erase our drawn plane
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     

    return result;
}


void CGfxOpenGL::Render()
{
    // enable z or depth buffer testing
    glEnable(GL_DEPTH_TEST);

    // clear screen and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     

    glMatrixMode(GL_PROJECTION);            // set projection matrix current matrix
    glLoadIdentity();                       // reset projection matrix

    // calculate aspect ratio of window
    gluPerspective(FOV, float(m_windowWidth) / m_windowHeight, 1.0f, 1000.0f);

    glMatrixMode(GL_MODELVIEW);             // set modelview matrix
    glLoadIdentity();                       // reset modelview matrix
    glRotatef(Roll, 0.0f, 0.0f, 1.0f);
	gluLookAt(PosX, PosY, PosZ, LookX, LookY, LookZ, 0, 1, 0);


	glPushMatrix();

        glPushMatrix();
            glTranslatef(-50.0, 0.0, 25.0f);
            xZFloor(-5);

        glPopMatrix();

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glEnable(GL_DEPTH_TEST);

        glTranslatef(SphereCenter.x, SphereCenter.y, 0);
        gluSphere(quadric, 1.0f, 20, 20);


    glPopMatrix();

	roboArm.Update(&SphereCenter);
	roboArm.Draw(quadric);

}